<?php

declare(strict_types=1);

namespace App\Websocket;

use App\Model\Session;
use Hyperf\Context\ApplicationContext;
use Hyperf\Engine\WebSocket\Response;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Psr\Log\LoggerInterface;
use Swoole\Server;
use Swoole\WebSocket\Frame;
use Hyperf\Engine\WebSocket\Frame as EngineFrame;

class DinnerMessageHandler
{
    private ?Server $server = null;

    private ?Frame $frame = null;

    private LoggerInterface $logger;

    private Response $response;

    private int $serverId = 0;

    private bool $echoOff = false;

    /**
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    public function __construct(Server $server, Frame $frame)
    {
        $this->server = $server;
        $this->frame = $frame;
        $this->logger = ApplicationContext::getContainer()->get(LoggerInterface::class);
        $this->response = (new Response($server))->init($frame);
        $masterPid = $server->master_pid;
        $this->serverId = GlobalContext::getContext($masterPid);
    }

    public function action()
    {
        $this->logger->info($this->frame->data);
        if (str_starts_with($this->frame->data, "cmd|")) {
            try {
                return $this->cmd();
            } catch (\Exception $e) {
                echo 'cmdError:' . $e->getTraceAsString();
                return '';
            }
        }
        echo '[' . date('Y-m-d H:i:s') . '] receive from ' . $this->frame->fd . ':' . $this->frame->data . "\n";
//        $this->jsonMessage();
    }

    private function response(string $message): bool
    {
        return $this->response->push(new EngineFrame(payloadData: $message));
    }

    /**
     * 系统指令处理
     * "cmd|001|{'x':'y'}"
     */
    private function cmd()
    {
        $cmd_data = substr($this->frame->data, 4);
        $cmd_type = substr($cmd_data, 0, 3);
        $cmd_content = substr($cmd_data, 4);
        if ($cmd_type == '001') {
            #001指令  指令内容 {"fd":"2","data":"xxx"}
            $content = json_decode($cmd_content, true);
            if (is_array($content['fd'])) {#多用户群发
                foreach ($content['fd'] as $v) {
                    $this->server->push($v, $content['data']);
                }
            } else {#单用户发送
                $this->server->push($content['fd'], $content['data']);
            }

            return $this->response('cmd success!');
        } elseif ($cmd_type == '002') {
            #002指令  指令内容 {"uid":["xx1","xx2","xx3"],"data":"xxx"}
            #指令必须指定 返回结果 同时只能收到一个客户端的响应结果
            $content = json_decode($cmd_content, true);
            $uids = is_array($content['uid']) ? $content['uid'] : [$content['uid']];
            $fd_arr = Session::where('server_id', $this->serverId)
                ->where('userid', 'in', $uids)
                ->value('fd');
            $data_json_arr = json_decode($content['data'], true);
            if (!empty($data_json_arr)) {//如果数据 是 json类型 自动补全 req_id
                $data_json_arr['req_id'] = $this->frame->fd;
                $content['data'] = json_encode($data_json_arr);
            }
            foreach ($fd_arr as $k => $v) {
                if ($k == 0) {
                    #只有第一个 连接才能 响应 返回结果监听  注册事件
                    Session::where('server_id', $this->serverId)
                        ->where('fd', $v)
                        ->update(['waiting_fd' => $this->frame->fd]);
                }
                $this->server->push($v, $content['data']);
            }
            return $this->response('cmd success!,uids=' . implode(',', $fd_arr));
        } elseif ($cmd_type == '003') {
            $this->echoOff = true;
            #003指令  指令内容 {"shop_id":'42',"data":"xxx"}
            #向收银机发送 消息
            $content = json_decode($cmd_content, true);
            $msg_id = WsMessage::addMessage($content['data']);
            try {
                $mqPro = new MqProductor(true);
                $content['data']['id'] = $msg_id;
                $mqPro->publish('ws.dinner.push', json_encode($content['data']));
            } catch (\Exception $e) {
                echo date("Y-m-d H:i:s") . $e->getMessage() . "\n";
            }
            $casher = Db::table('casher_room')->where(['room_uid' => 'v1.0_' . $content['shop_id']])->find();
            if (empty($casher)) {
                $this->push($this->frame->fd, "cmd\r\nfail\r\n收银机没有登陆");
                return;
            }
            #如果data是个json类型 则data数据中插入 req_id信息
            if (is_array($content['data'])) {
                $content['data']['req_id'] = $this->frame->fd;
            }
            $ret = $this->server->push($casher['fd'], json_encode($content['data']));
            if ($ret === false) {
                $this->server->push($this->frame->fd, "cmd\r\nfail\r\n向收银机发送数据失败");
                return;
            }
            $this->server->push($this->frame->fd, "cmd\r\nsucceed");
            return;
        } elseif ($cmd_type == '004') {
            #003指令  指令内容 {"shop_id":'42',"table_id":"2","data":"xxx"}
            #向web扫码点餐前端用户广播发送 消息
            $content = json_decode($cmd_content, true);
            $clist = Db::table('customer_room')->where(
                ['room_uid' => 'v1.0_' . $content['shop_id'] . '_' . $content['table_id']]
            )->select();
            foreach ($clist as $v) {
                $ret = $this->server->push($v['fd'], $content['data']);
            }
            return "cmd\r\nsucceed\r\n" . count($clist);
        } elseif ($cmd_type == '005') {
            $this->server->push($this->frame->fd, 'restart...');
            $this->server->reload();
        } elseif ($cmd_type == '006') {
            $content = json_decode($cmd_content, true);
            #不重新添加wsmessage 向收银机发送 消息
            $casher = Db::table('casher_room')->where(['room_uid' => 'v1.0_' . $content['shop_id']])->find();
            if (empty($casher)) {
                $this->push($this->frame->fd, "cmd\r\nfail\r\n收银机没有登陆");
                return;
            }
            #如果data是个json类型 则data数据中插入 req_id信息
            if (is_array($content['data'])) {
                $content['data']['req_id'] = $this->frame->fd;
            }
            $ret = $this->server->push($casher['fd'], json_encode($content['data']));
            if ($ret === false) {
                $this->server->push($this->frame->fd, "cmd\r\nfail\r\n向收银机发送数据失败");
                return;
            }
            $this->server->push($this->frame->fd, "cmd\r\nsucceed");
            return;
        }
    }
}
